#ifndef SRVCONNECTION_H_
#define SRVCONNECTION_H_

#include "scHeader.h"
#include <QObject>
#include <QBuffer>
#include "StateConnection.h"
#include "StateRequest.h"

namespace sybilpm
{
/**
 * This class acts as a proxy of the server. Protocol issues are encapsulated here,
 * so no http specifics outside this class. It simulates a continuous connection
 * although the underlying protocol may open and close the connection multiple times.
 */
class SrvConnection : public QObject
{
	Q_OBJECT
signals:
	/**
	 * This signal is emitted when the http engine starts a new action.
	 * For informational purpose only since it may depend on
	 * the platform you are running on.
	 * Is disabled by default for performance reasons and can be enabled
	 * any time with setVerbose(true).
	 *
	 * @param text the informational test (localized for non-tech terms)
	 */
	void infoActionText(QString text);

	/**
	 * This signal is emitted when the http engine changes its internal state.
	 * For informational purpose only since it may depend on the platform you
	 * are running on.
	 * Is disabled by default for performance reasons and can be enabled
	 * any time with setVerbose(true).
	 *
	 * @param text the informational test (localized for non-tech terms)
	 */

	void infoStatusText(QString text);

	/**
	 * The requested server test was completed. Note that you have to free
	 * the transaction data using freeData.
	 *
	 * @param success true, if the connector is now able to accept requests
	 * @param transId Data of this transaction contains the header information of the server.
	 * 	If there is no such information transId is 0xffffffff.
	 * @see startTest freeData
	 */
	void testReadySig(bool success, unsigned long transId);

	/**
	 * Emitted when a transaction completed successfully. Note that you have to free
	 * the transaction data using freeData. This signal can be emitted before startRequest
	 * returns, that is the calling party may not know transId yet. (This will happen
	 * if the request can be served synchronously during startRequest.)
	 *
	 * @param transId id of the completed transaction
	 * @see startRequest
	 */
	void dataReadySig(unsigned long transId);

	/**
	 * Emitted when a transaction completed unsuccessfully. The buffers associated
	 * with the transaction are automatically (and silently, no
	 * transactionDeleteSig) freed.
	 * There is no built-in retry mechanism.
	 * A failed transaction usually turns the server connection state to failed too.
	 * See the comment about "unknown" transaction IDs in dataReadySig.
	 *
	 * @param transId id of the failed transaction
	 * @see startRequest, dataReadySig
	 */
	void requestFailedSig(unsigned long transId);

	/**
	 * A certain data buffer was requested to be removed and cannot be accessed
	 * any more.
	 *
	 * @param transId id of the transaction whose buffer was deleted
	 */
	void transactionDeleteSig(unsigned long transId);

	/**
	 * Emitted when the availability of the connection changes.
	 *
	 * @param available true if the connection became available, false if
	 * 	we lost connection
	 */
	void connectionAvailableSig(bool available);

	/**
	 * All pointers to data buffers and pending request will become invalid soon.
	 * This signal is usually a consequence of deleting the server connection.
	 *
	 * @param ident identification of the instance going to vanish
	 * @see getIdentity
	 */
	void connectionShutdownSig(void * ident);

public:
	/**
	 * Factory based on saved application settings.
	 */
	static SrvConnection * createFromSettings();

	/**
	 * Delete the connection. This will abort all pending and active request
	 * and delete all request buffers. connectionShutdownSig is emitted.
	 */
	virtual ~SrvConnection();

	/**
	 * Creates a unique transaction ID every time called.
	 */
	unsigned long createTransactionId();

	/**
	 * Get a user readable representation of the latest error.
	 */
	virtual QString errorString() const;

	/**
	 * You need this value to check it against the parameter of connectionShutdownSig.
	 * So you will know if someone exchanged the server connection behind the scene. If
	 * this happens you lose all buffers and pending transactions.
	 *
	 * @return A unique identification of the server connection.
	 */
	virtual const void * getIdentity() const {return this;}

	/**
	 * Test if requests can be started. If you try to start a request
	 * if this method says "false" you may be rewarded with an exception.
	 * Of course, this method cannot guarantee that your request will
	 * complete successfully, it just means that you can _start_ the
	 * request.
	 * The answer stays valid up to (and of course including) the next
	 * call to any method of this object. Do not save the result for
	 * future use!
	 *
	 * @return true, if the connector accepts requests
	 */
	virtual bool acceptRequest() const =0;

	/**
	 * Test if a test can be started. If you try to start a test
	 * if this method says "false" you may be rewarded with an exception.
	 * The answer stays valid up to (and of course including) the next
	 * call to any method of this object. Do not save the result for
	 * future use!
	 *
	 * @return true, if the connector can perform a server test
	 */
	virtual bool acceptTest() const =0;

	/**
	 * Check if a test is underway.
	 *
	 * @return true if currently testing, false otherwise
	 */
	virtual bool isTesting() const =0;

	/**
	 * Check if we can talk with the server. This method can be used to recover
	 * from the failed state. Check acceptTest before you try to start a test.
	 * Performing a test is the usual way get from setup state (after creation)
	 * to an idle state where requests can be started. Listen to testReadySig
	 * because this is an asynchronous action.
	 *
	 * @return the transaction ID of the test
	 * @see acceptTest testReadySig
	 */
	virtual unsigned long startTest() =0;

	/**
	 * Request data from the server. Check acceptRequest before you try to start
	 * a request. Listen to dataReadySig and requestFailedSig if the return value
	 * is on-zero because this is an asynchronous action then.
	 *
	 * @param requDesc contains the details of the request; may be destroyed after
	 * 	the method returns
	 * @param transactionId you may supply your preferred trans-ID here; 0 will
	 *  create one internally; use trans-IDs ONLY ONCE; you must obtain it from
	 *  createTransactionId; if you supply an ID the method guarantees to return
	 *  either 0 or your ID
	 * @return the transaction ID of the request or 0 if served synchronously
	 * @see acceptRequest dataReadySig requestFailedSig
	 */
	virtual unsigned long startRequest(RequestDescriptor * requDesc, unsigned long transactionId =0, bool bootTransaction =false) =0;

	/**
	 * After receiving dataReadySig you can use this method to access the data buffer.
	 * If you try to access data earlier, you may get a NULL pointer or a
	 * buffer object filled with invalid data.
	 *
	 * @param transId the transaction ID of the request you are interested in
	 * @return a buffer containing the payload of the request
	 * @see dataReadySig freeData
	 */
	virtual QBuffer * getData(unsigned long transId) =0;

	/**
	 * After processing the data you have to free the buffer with this method. Failing
	 * in doing so will result in a logical MEMORY LEAK - SrvConnection does not know
	 * when the requester is done with the data so it can never free it on its own
	 * decision.
	 * The call will trigger a bufferDeleteSig. If there are multiple receivers for
	 * dataReadySig you have to make sure that no-one tries to access it after
	 * bufferDeleteSig was issued.
	 * You cannot use freeData to abort a pending or active transaction! Doing so
	 * will cause an exception (invalid state transition).
	 * You may call this method multiple times for the same transaction. All calls after
	 * the first one are silently ignored. (Useful if there are more than one listener to
	 * dataReadySig.)
	 * Calling freeData with transId 0xFFFFFFFF is permitted and does nothing.
	 *
	 * @transId the transaction you are referring to
	 * @see abortAllTransactions startRequest bufferDeleteSig
	 */
	virtual void freeData(unsigned long transId) =0;

	/**
	 * Abort and delete all active and pending transactions. Already completed
	 * transactions are not touched and their buffers stay valid. You cannot abort
	 * a specific transaction while leaving other transactions in the queue.
	 */
	virtual void abortAllTransactions() =0;

	/**
	 * The server version as read during the latest test. You can call
	 * this methode even before the first test but you will get all numbers
	 * as -1 and an empty text.
	 *
	 * @param versionText a human readable text like "V 1.2.3", not localized
	 * @param major the major version or -1 if still unknown
	 * @param minor the minor version or -1 if still unknown
	 * @param build the build number or -1 if still unknown
	 */
	void getVersionInfo(QString * versionText, int * major, int * minor, int * build) const;

	/**
	 * Allow or suppress infoActionText and infoStatusText signals.
	 *
	 * @param verbose false will suppress all info... signals
	 * @see infoActionText infoStatusText
	 */
	void setVerbose(bool verbose) {verb=verbose;}

protected:
	/**
	 * Default constructor for use by implementation classes.
	 */
	SrvConnection();

	int versMaj, versMin, versBld;
	bool verb;
	QString localError;

private:
	static unsigned long nextTrans;
};

} // namespace sybilpm

#endif /*SRVCONNECTION_H_*/
